"use client"; import { use } from "react"; import Link from "next/link"; import { ExternalLink } from "lucide-react"; import { useApi } from "@/lib/api"; import { fmtDate, fmtDuration, fmtNum, truncate } from "@/lib/format"; import { Empty, Metric, Panel, Skeleton, SurfaceChips, Table, Tag, TypeTag } from "@/components/ui"; import type { EntityRow } from "@/components/EntityTable"; interface Detail { entity: EntityRow & { first_seen: string; last_seen: string; seen_count: number; first_session: string }; observations: { session_id: string; step: number; ts: string; surfaces: string[]; snapshot: { name?: string; author?: string; metrics?: Record; context?: string; provenance?: { surface: string; confidence: number; detail?: string }[] }; goal: string | null }[]; relations: { rel_type: string; from_fp: string; to_fp: string; from_name: string | null; to_name: string | null; from_type: string; to_type: string; step: number }[]; media: { duration_s: number | null; width: number | null; height: number | null; delivery: { kind: string; hostnames: string[] } | null; frames: string[] | null; thumbnail_url: string | null } | null; feed: { session_id: string; step: number; page_type: string; feed_position: number; visible: boolean; ts: string }[]; } export default function EntityPage({ params }: { params: Promise<{ fp: string }> }) { const { fp } = use(params); const { data, loading, error } = useApi(`/entities/${encodeURIComponent(decodeURIComponent(fp))}`); if (loading) return ; if (error || !data) return {error ?? "not found"}; const e = data.entity; const type = e.entity_type ?? e.type ?? "?"; return (
entities / {e.fingerprint}
{e.platform} {e.url && ( open on platform )}

{e.name ?? e.text_excerpt ?? e.platform_id}

{e.author &&
by {e.author}
} {e.text_excerpt && e.name &&

{e.text_excerpt}

}
{Object.entries(e.fields ?? {}).map(([k, f]) => ( ))}
{k} {typeof f.value === "string" ? f.value : JSON.stringify(f.value)} {f.provenance?.[0]?.detail &&
{f.provenance[0].detail}
}
o.session_id)).size} session(s)`} /> {e.metrics && Object.entries(e.metrics).map(([k, v]) => ( ))} {data.media && ( <> {data.media.width ? : null} {data.media.delivery && } )} {data.media?.frames?.length ? (
{data.media.frames.map((f) => ( // eslint-disable-next-line @next/next/no-img-element ))}
) : data.media?.thumbnail_url || e.media?.thumbnail_url ? ( {/* eslint-disable-next-line @next/next/no-img-element */} ) : null}
{data.observations.map((o, i) => ( ))}
{fmtDate(o.ts)} {truncate(o.goal ?? o.session_id, 32)} #{o.step} ({ surface: s, confidence: 0 }))} /> {o.snapshot.context} {o.snapshot.name}
{data.relations.length ? ( {data.relations.map((r, i) => ( ))}
{truncate(r.from_name ?? r.from_fp, 40)} {r.rel_type} {truncate(r.to_name ?? r.to_fp, 40)}
) : ( no relationships recorded )}
{data.feed.length ? ( {data.feed.map((f, i) => ( ))}
{fmtDate(f.ts)} {f.page_type} #{f.feed_position + 1} {f.visible ? "yes" : "below fold"}
) : ( not seen in a feed )}
); }